package br.com.zynger.mst.addressbook;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/**
 * An address book holds a collection of
 * entries, each recording a person's first
 * and last names, address, city, state, zip
 * and phone number.
 * 
 * It is possible to add, edit, remove and
 * sort the entries by name or zip code.
 * 
 * @author J. Zynger
 * @version 1.0 - November 2012 */

public class AddressBook {

	private List<Person> entries;
	
	public AddressBook() {
		setEntries(new ArrayList<Person>());
	}
	
	/**
	 * Add the person to the address book's entry list.
	 * 
	 * If there is a person with the same name, this
	 * person will be edited with the new data inputted.
	 * 
	 * @param person The person to add.
	 * 
	 * @return <code>true</code> if the person was correctly
	 * added (or edited) into the address book's entries, or
	 * <code>false</code> otherwise.
	 */
	public boolean addPerson(Person person) {
		if(null == getPerson(person.getFirstName(), person.getLastName()))
			return entries.add(person);
		else return editPerson(person);
	}
	
	public boolean removePerson(Person p){
		return entries.remove(p);
	}
	
	/**
	 * Look into the address book's entries for the person
	 * with the first and last name inputted.
	 * 
	 * @param firstName The person's first name
	 * @param lastName The person's last name
	 * 
	 * @return The searched person or <code>null</code>
	 * if the person doesn't exist in the address book.
	 */
	public Person getPerson(String firstName, String lastName){
		for (Iterator<Person> iterator = entries.iterator(); iterator.hasNext();) {
			Person person = (Person) iterator.next();
			//Refactoring 1: Extract Method
			//Refactoring 2: Move Method (moved to Person.java)
			if(person.isSamePerson(firstName, lastName)) return person;
		}
		return null;
	}
	
	/**
	 * Remove the inputted person's old entry in the
	 * address book, and add the new reference.
	 * 
	 * @param person The person to be substituted.
	 * 
	 * @return <code>true</code> if the person was
	 * successfully edited, or <code>false</code>
	 * if the person could not be found in the address
	 * book's entries.
	 */
	public boolean editPerson(Person person) {
		if(null != person){			
			Person addressBookPerson = getPerson(person.getFirstName(), person.getLastName());
			if(null == addressBookPerson) return false;
			else{
				//Refactoring 3: Remove extra testing for boolean return
				if(removePerson(addressBookPerson)){
					addPerson(person);
					return true;
				}else return false;
			}
		}else return false;
	}
	
	public void sortByName(){
		Collections.sort(entries, new NameComparator());
	}
	
	public void sortByZipCode(){
		Collections.sort(entries, new ZipCodeComparator());
	}
	
	/**
	 * The ordering imposed by the NameComparator
	 * in a set of objects of the type Person
	 * will follow the alphabetical ordering of
	 * their last names. If those are equal, the
	 * ordering will follow the alphabetical
	 * ordering of their first names.
	 * 
	 * @author J. Zynger
	 * @version 1.0 - November 2012 */
	private class NameComparator implements Comparator<Person> {
		@Override
		public int compare(Person lhs, Person rhs) {
			int result = lhs.getLastName().compareTo(rhs.getLastName());
			if(0 == result){
				return lhs.getFirstName().compareTo(rhs.getFirstName());
			}else return result;
		}
	}
	
	/**
	 * The ordering imposed by the ZipCodeComparator
	 * in a set of objects of the type Person will
	 * follow the numerical ordering of their zip codes.
	 * If those are equal, the ordering will follow the
	 * ordering imposed by a NameComparator. 
	 * 
	 * @author J. Zynger
	 * @version 1.0 - November 2012
	 * @see NameComparator */
	private class ZipCodeComparator implements Comparator<Person> {
		@Override
		public int compare(Person lhs, Person rhs) {
			int result = lhs.getZipCode().compareTo(rhs.getZipCode());
			if(0 == result){
				return new NameComparator().compare(lhs, rhs);
			}else return result;
		}
	}
	
	public List<Person> getEntries() {
		return entries;
	}
	
	private void setEntries(List<Person> entries) {
		this.entries = entries;
	}

}
